using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace TankWars
{
    /// <summary>
    /// Abstract class for all movable sprites
    /// </summary>
    public abstract class MovableSprite : SpriteAnimation
    {
        public Vector2 vAccel = Vector2.Zero;
        public Vector2 vSpeed = Vector2.Zero;
        public Vector2 vPosition = Vector2.Zero;

        public float fRotation = 0.0f;

        //Is the object on high ground?
        public bool highGround = false;

        protected Color cSpriteColor = Color.White;

        public MovableSprite(Texture2D spriteTexture) 
            : base(spriteTexture)
        {
        }

        public MovableSprite(string strSpriteName, Texture2D spriteTexture, string strFrameXMLFile) 
            : base(strSpriteName, spriteTexture, strFrameXMLFile)
        {
        }

        /// <summary>
        /// Updates the position based on the time elapsed and the speed
        /// </summary>
        /// <param name="gameTime"></param>
        public virtual void UpdatePosition(GameTime gameTime)
        {
            //Get previous Speed.
            Vector2 vSpeed0 = this.vSpeed;

            //Update the object's speed.
            this.vSpeed += this.vAccel * (float)gameTime.ElapsedGameTime.TotalSeconds;

            //Calculate the average velocity.
            Vector2 vAvgSpeed = (this.vSpeed + vSpeed0) / 2;

            //Determine the object's new position.
            this.vPosition += vAvgSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;

            base.ContinueAnimation(gameTime);
        }

        /// <summary>
        /// Draws the sprite with the SpriteBatch 
        /// </summary>
        /// <param name="spriteBatch"></param>
        public virtual void Draw(SpriteBatch spriteBatch, Map map)
        {
            //spriteBatch.Draw(this.Sprite, map.GetTrueScreenPosition(this.vPosition), null, this.cSpriteColor, this.fRotation, this.Origin, 1.0f, SpriteEffects.None, 0.0f);
            base.Draw(spriteBatch, map.GetTrueScreenPosition(this.vPosition), this.fRotation, this.Origin, this.cSpriteColor);
        }

        /// <summary>
        /// Get a vector perpendicular to the input vector
        /// </summary>
        /// <param name="inputVector">Input vector</param>
        /// <returns></returns>
        public static Vector2 GetPerpendicularVector(Vector2 inputVector)
        {
            float fPerpendicularVectorY = (float)Math.Sqrt(inputVector.Length() / (float)(Math.Pow(Convert.ToDouble(-inputVector.Y / inputVector.X), 2) + 1));
            float fPerpendicularVectorX = (-inputVector.Y * fPerpendicularVectorY) / inputVector.X;

            return new Vector2(fPerpendicularVectorX, fPerpendicularVectorY);
        }

        /// <summary>
        /// Returns the points that make up a rectangular sprite.
        /// </summary>
        /// <returns></returns>
        public Vector2[] getPoints()
        {
            Vector2[] point = new Vector2[4];

            //Get top left point.
            float x = (-1) * this.SpriteWidth / 2;
            float y = (-1) * this.SpriteHeight / 2;
            float r = (float)Math.Sqrt(x * x + y * y);
            float cosine = (float)(Math.Cos((fRotation + Math.Atan(y / x))));
            float sine = (float)(Math.Sin((fRotation + Math.Atan(y / x))));
            point[0].X = (-1) * r * cosine + vPosition.X;
            point[0].Y = (-1) * r * sine + vPosition.Y;

            //Get top right point
            x = this.SpriteWidth / 2;
            r = (float)Math.Sqrt(x * x + y * y);
            cosine = (float)(Math.Cos((fRotation + Math.Atan(y / x))));
            sine = (float)(Math.Sin((fRotation + Math.Atan(y / x))));
            point[1].X = r * cosine + vPosition.X;
            point[1].Y = r * sine + vPosition.Y;

            //Get bottom right point
            y = this.SpriteHeight / 2;
            r = (float)Math.Sqrt(x * x + y * y);
            cosine = (float)(Math.Cos((fRotation + Math.Atan(y / x))));
            sine = (float)(Math.Sin((fRotation + Math.Atan(y / x))));
            point[2].X = r * cosine + vPosition.X;
            point[2].Y = r * sine + vPosition.Y;

            //Get bottom left point
            x = (-1) * this.SpriteWidth / 2;
            r = (float)Math.Sqrt(x * x + y * y);
            cosine = (float)(Math.Cos((fRotation + Math.Atan(y / x))));
            sine = (float)(Math.Sin((fRotation + Math.Atan(y / x))));
            point[3].X = (-1) * r * cosine + vPosition.X;
            point[3].Y = (-1) * r * sine + vPosition.Y;

            return point;
        }

        /// <summary>
        /// Simple collision that checks a collision between points and a bounding box.
        /// This collision method is the quickest and is used between bullets and
        /// the field's collidable blocks.  Not as roubust as the shapecollision
        /// method but this one is much more efficient.
        /// </summary>
        /// <returns></returns>
        public static bool simpleCollision(Vector2[] points, Vector2[] box)
        {
            //The second parameter must have a length of four.
            if (box.Length != 4)
                return false;

            //Check all points to see if they lie within the bounding box.
            for (int n=0;n<points.Length;n++)
            if ((points[n].X > box[0].X)&&(points[n].X < box[2].X))
            if ((points[n].Y > box[0].Y)&&(points[n].Y < box[2].Y))
                return true;

            return false;
        }

        /// <summary>
        /// Method checks to see if two polygons have collided.
        /// </summary>
        /// <param name="shape1"> A linked list of points that creates a shape</param>
        /// <param name="shape2">The second shape that is being checked for collisions.</param>
        /// <returns>Returns bool true if they have collided.</returns>
        public static bool shapeCollision(Vector2[] shape1, Vector2[] shape2)
        {
            //Go through all the points in shape1.
            for (int p1 = 0; p1 < shape1.Length; p1++)
            {
                //Go through all the points in shape2
                for (int p2 = 0; p2 < shape2.Length; p2++)
                {
                    //Connecting points will be the next point in the shape.
                    int cp1 = p1 + 1;
                    int cp2 = p2 + 1;

                    //If p1 is at the last point, the connect point will be 0
                    if (p1 == shape1.Length - 1)
                    {
                        //Last line doesn't need to be check for shapes with two points.
                        if (shape1.Length == 2) break;

                        cp1 = 0;
                    }

                    //If at last point in shape2.
                    if (p2 == shape2.Length - 1)
                    {
                        //Last line doesn't need to be check for shapes with two points.
                        if (shape2.Length == 2) break;
                        cp2 = 0;
                    }

                    if (lineIntersect(shape1[p1], shape1[cp1], shape2[p2], shape2[cp2]))
                        return true;
                }//For: shape2 points
            }//For: shape1 points

            return false;
        }

        /// <summary>
        /// Determines the point of intersection between two line segments, 
        /// as defined by four points.
        /// </summary>
        /// <param name="a">The first point on the first line segment.</param>
        /// <param name="b">The second point on the first line segment.</param>
        /// <param name="c">The first point on the second line segment.</param>
        /// <param name="d">The second point on the second line segment.</param>
        /// <param name="point">The output value with the interesection, if any.</param>
        /// <remarks>The output parameter "point" is only valid
        /// when the return value is true.</remarks>
        /// <returns>True if intersecting, false otherwise.</returns>
        public static bool lineIntersect(Vector2 a, Vector2 b, Vector2 c, Vector2 d)//, out Vector2 point)
        {
            //point = Vector2.Zero;

            double r, s;
            double denominator = (b.X - a.X) * (d.Y - c.Y) - (b.Y - a.Y) * (d.X - c.X);

            // If the denominator in above is zero, AB & CD are colinear
            if (denominator == 0)
            {
                return false;
            }

            double numeratorR = (a.Y - c.Y) * (d.X - c.X) - (a.X - c.X) * (d.Y - c.Y);
            r = numeratorR / denominator;

            double numeratorS = (a.Y - c.Y) * (b.X - a.X) - (a.X - c.X) * (b.Y - a.Y);
            s = numeratorS / denominator;

            // non-intersecting
            if (r < 0 || r > 1 || s < 0 || s > 1)
            {
                return false;
            }

            // find intersection point
            //point.X = (float)(a.X + (r * (b.X - a.X)));
            // point.Y = (float)(a.Y + (r * (b.Y - a.Y)));

            return true;
        }
    }
}
